/*
 *  (c) 2015 fullhan.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */
#include <common.h>
#include <command.h>
#include <net.h>
#include <malloc.h>
#include <linux/compiler.h>
#include <miiphy.h>
#include <linux/mii.h>
#include "fh_gmac.h"
#include <phy.h>

#define FH_GMAC_ASSERT(expr) if (!(expr)) { \
        printf("Assertion failed! %s:line %d\n", \
        __func__, __LINE__); \
        }

//GMAC-MAC
#define		REG_GMAC_CONFIG				(0x0000)
#define		REG_GMAC_FRAME_FILTER		(0x0004)
#define		REG_GMAC_GMII_ADDRESS		(0x0010)
#define		REG_GMAC_GMII_DATA			(0x0014)
#define		REG_GMAC_DEBUG				(0x0024)
#define		REG_GMAC_MAC_HIGH 			(0x0040)
#define		REG_GMAC_MAC_LOW 			(0x0044)

//GMAC-DMA
#define		REG_GMAC_BUS_MODE			(0x1000)
#define		REG_GMAC_TX_POLL_DEMAND		(0x1004)
#define		REG_GMAC_RX_POLL_DEMAND		(0x1008)
#define		REG_GMAC_RX_DESC_ADDR		(0x100C)
#define		REG_GMAC_TX_DESC_ADDR		(0x1010)
#define		REG_GMAC_STATUS				(0x1014)
#define		REG_GMAC_OP_MODE			(0x1018)
#define		REG_GMAC_INTR_EN			(0x101C)
#define		REG_GMAC_ERROR_COUNT		(0x1020)
#define		REG_GMAC_AXI_BUS_MODE		(0x1028)
#define		REG_GMAC_AXI_STATUS			(0x102C)
#define 	REG_GMAC_CURR_TX_DESC		(0x1048)
#define 	REG_GMAC_CURR_RX_DESC		(0x104C)

static void fh_set_rmii_speed(Gmac_Object *pGmac, int speed);
extern char *fh_pinctrl_smux_backup(char *devname, char *muxname, int muxsel);
extern char *fh_pinctrl_smux_restore(char *devname, char *muxname, int muxsel);
extern int fh_pinctrl_set_oe(char *pin_name, unsigned int oe);
extern int fh_pinctrl_sdev(char *devname, unsigned int flag);
extern int fh_pinctrl_smux(char *devname, char* muxname, int muxsel, unsigned int flag);
static unsigned int mac_idx = 0;

static void GMAC_SetMacAddress(Gmac_Object* gmac, UINT8* mac)
{
	UINT32 macHigh = mac[5] << 8 | mac[4];
	UINT32 macLow = mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0];
	SET_REG(gmac->regs + REG_GMAC_MAC_HIGH, macHigh);
	SET_REG(gmac->regs + REG_GMAC_MAC_LOW, macLow);
}

static void GMAC_SetPhyRegister(Gmac_Object* gmac, int phyid, int regNum, UINT32 data)
{
	/*printf("[PHY WRITE] :: ADDR : REG : DATA = %d : %d : %x\n",phyid, regNum, data);*/
	SET_REG(gmac->regs + REG_GMAC_GMII_DATA, data);
	SET_REG(gmac->regs + REG_GMAC_GMII_ADDRESS,
			0x1 << 1 | phyid << 11 | gmac_gmii_clock_100_150 << 2
					| regNum << 6 | 0x1);
	while (GET_REG(gmac->regs + REG_GMAC_GMII_ADDRESS) & 0x1) {

	}
}

static UINT32 GMAC_GetPhyRegister(Gmac_Object* gmac, int phyid, int regNum)
{
	UINT32 ret;
	SET_REG(gmac->regs + REG_GMAC_GMII_ADDRESS,
			phyid << 11 | gmac_gmii_clock_100_150 << 2 | regNum << 6 | 0x1);
	while (GET_REG(gmac->regs + REG_GMAC_GMII_ADDRESS) & 0x1) {

	}
	ret  = GET_REG(gmac->regs + REG_GMAC_GMII_DATA);
	/*printf("[PHY READ] :: ADDR : REG : DATA = %d : %d : %x\n",phyid, regNum, ret);*/
	return ret;
}


int fh_gmac_miiphy_read (struct mii_dev *bus, int addr,
		 int devad, int reg)
{
	u16 ret;
	ret = (unsigned short)GMAC_GetPhyRegister(bus->priv, addr, reg);
	return ret;
}

int fh_gmac_miiphy_write (struct mii_dev *bus, int addr, int devad,
		int reg, unsigned short value)
{

	GMAC_SetPhyRegister(bus->priv, addr, reg, value);

	return 0;
}

static void GMAC_DmaTxDescriptorInit(Gmac_Object* gmac)
{
	gmac->tx_dma_descriptors->desc0.dw = 0;
	gmac->tx_dma_descriptors->desc1.dw = 0;
	gmac->tx_dma_descriptors->desc2.dw = 0;
	gmac->tx_dma_descriptors->desc3.dw = 0;

	gmac->tx_dma_descriptors->desc1.bit.first_segment = 1;
	gmac->tx_dma_descriptors->desc1.bit.last_segment = 1;
	gmac->tx_dma_descriptors->desc1.bit.end_of_ring = 1;
	gmac->tx_dma_descriptors->desc1.bit.second_address_chained = 0;
	gmac->tx_dma_descriptors->desc3.bit.buffer_address_pointer =
			(UINT32) gmac->tx_dma_descriptors;
}

static void GMAC_DmaRxDescriptorInit(Gmac_Object* gmac)
{
	#if(1)
	int i;
	for (i = 0; i < RX_DESC_NUM; i++) {
		gmac->rx_dma_descriptors[i].desc0.dw = 0;
		gmac->rx_dma_descriptors[i].desc1.dw = 0;
		gmac->rx_dma_descriptors[i].desc2.dw = 0;
		gmac->rx_dma_descriptors[i].desc3.dw = 0;
		gmac->rx_dma_descriptors[i].desc1.bit.buffer1_size = 1536;
		gmac->rx_dma_descriptors[i].desc1.bit.end_of_ring = 0;
		gmac->rx_dma_descriptors[i].desc1.bit.second_address_chained = 1;
		gmac->rx_dma_descriptors[i].desc3.bit.buffer_address_pointer =
				(UINT32) &gmac->rx_dma_descriptors[i + 1];
		gmac->rx_dma_descriptors[i].desc2.bit.buffer_address_pointer =
				(UINT32) (net_rx_packets[i]);
		gmac->rx_dma_descriptors[i].desc0.bit.own = 1;

	}
	gmac->rx_dma_descriptors[RX_DESC_NUM - 1].desc3.bit.buffer_address_pointer =
			(UINT32) &gmac->rx_dma_descriptors[0];
	gmac->rx_dma_descriptors[RX_DESC_NUM - 1].desc1.bit.end_of_ring = 1;
	#endif
}

void GMAC_PhyInit(Gmac_Object* gmac)
{
	struct phy_reg_cfg *p_cfg;
	struct phy_reg_cfg_list *p_cfg_data;
	u32 ret;

	p_cfg = (struct phy_reg_cfg *)gmac->p_plat->p_cfg_array;
	for(; p_cfg->id != 0; p_cfg++){
		if(p_cfg->id == gmac->oui){
			/*printf("find phy id [%08x] reg para\n",p_cfg->id);*/
			for(p_cfg_data = p_cfg->list; p_cfg_data->r_w != 0; p_cfg_data++){
				if(p_cfg_data->r_w == W_PHY){
					GMAC_SetPhyRegister(gmac, gmac->phy_id, p_cfg_data->reg_add, p_cfg_data->reg_val);
				}else if(p_cfg_data->r_w == M_PHY){
					ret = GMAC_GetPhyRegister(gmac, gmac->phy_id, p_cfg_data->reg_add);
					ret &= ~(p_cfg_data->reg_mask);
					ret |= (p_cfg_data->reg_val & p_cfg_data->reg_mask);
					GMAC_SetPhyRegister(gmac, gmac->phy_id, p_cfg_data->reg_add, ret);
				}
			}
			/*find one match;break*/
			break;
		}
	}
}

void init_mac_addr(Gmac_Object* gmac, struct eth_device* dev) {
	UINT8 mac[6] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
	char* s;
	char* e;
	int i;

	s = getenv("ethaddr");
	if(s){
		for (i = 0; i < 6; i++) {
			dev->enetaddr[i] = s ? simple_strtoul(s, &e, 16) : 0;
			if (s)
				s = (*e) ? e + 1 : e;
		}
	}else
		memcpy(dev->enetaddr, mac, 6);

	memcpy(gmac->local_mac_address, dev->enetaddr, 6);
	printf("MAC: %pM\n", dev->enetaddr);
}

int GMAC_DmaInit(Gmac_Object* gmac) {

	//confirm that all previously initiated (before software-reset) or ongoing AHB or AXI transactions are complete.
	while (GET_REG(gmac->regs + REG_GMAC_AXI_STATUS) & 0x3) {
		//axi_status = GET_REG(REG_GMAC_AXI_STATUS);
	}
	//initialize dma bus mode reg0
	//address_aligned_beats
	//fixed_burst
	//burst_length = 32
	SET_REG(gmac->regs + REG_GMAC_BUS_MODE, 1 << 25 | 1 << 16 | 32 << 8);

	//init dma descriptor reg 3 & 4
	GMAC_DmaTxDescriptorInit(gmac);
	GMAC_DmaRxDescriptorInit(gmac);
	SET_REG(gmac->regs + REG_GMAC_RX_DESC_ADDR, (UINT32 )gmac->rx_dma_descriptors);
	SET_REG(gmac->regs + REG_GMAC_TX_DESC_ADDR, (UINT32 )gmac->tx_dma_descriptors);

	//op mode, reg 6
	//transmit_store_forward
	//receive_store_forward
	//operate_on_second_frame
	SET_REG(gmac->regs + REG_GMAC_OP_MODE, 0 << 25 | 0 << 21 | 0 << 2);

	//clear & disable all interrupts
	SET_REG(gmac->regs + REG_GMAC_STATUS, 0xffffffff);

	//After a hardware or software reset, all interrupts are disabled
	//normal_interrupt_summary
	//receive_complete
	//SET_REG(REG_GMAC_INTR_EN, 1<<16 | 1<<15 | 1<<7 | 1<<6);
	SET_REG(gmac->regs + REG_GMAC_INTR_EN, 0xffffbaff);

	//start tx & rx
	//start_stop_receive
	//start_stop_transmission_cmd
	//SET_REG(REG_GMAC_OP_MODE, 1<<13 | 1<<1);
	return 0;

}

static void fh_set_rmii_speed(Gmac_Object *pGmac, int speed)
{
	if(pGmac->p_phy_dri->ac_phy_info->sync_mac_spd){
		pGmac->p_phy_dri->ac_phy_info->sync_mac_spd(speed);
	}
}

void dump_phy_info(struct phy_device *p_phy){

	printf("[Addr] : %d\n[Phy Id] : %x\n[Advertising] : %x\n[Supported] : %x\n",
	p_phy->addr, p_phy->phy_id, p_phy->advertising, p_phy->supported);

	printf("[Link] is %s\n",
	p_phy->link == 1 ?"up" : "down");

	printf("[Negotiation] is %s\n",
	p_phy->autoneg == AUTONEG_ENABLE ?"enable" : "diable");

	printf("[Speed] is %s\n",
	p_phy->speed == SPEED_100 ?"100M" : "10M");

	printf("[Duplex] is %s\n",
	p_phy->duplex == DUPLEX_FULL ?"Full" : "Half");
}

static void fh_gmac_sync_phy_status(Gmac_Object* gmac){
	struct phy_device *p_phy;
	p_phy = gmac->p_phy_dev;
	/*dump_phy_info(p_phy);*/
	if(p_phy->speed == SPEED_100)
		gmac->speed = GMAC_SPEED_100M;
	else
		gmac->speed = GMAC_SPEED_10M;

	if(p_phy->duplex == DUPLEX_FULL)
		gmac->duplex = GMAC_DUPLEX_FULL;
	else
		gmac->duplex = GMAC_DUPLEX_HALF;

	if (p_phy->link == 1) {
		if(gmac->p_phy_dri->ac_phy_info->brd_link_up_cb)
			gmac->p_phy_dri->ac_phy_info->brd_link_up_cb((void *)gmac);
	} else {
		if(gmac->p_phy_dri->ac_phy_info->brd_link_down_cb)
			gmac->p_phy_dri->ac_phy_info->brd_link_down_cb((void *)gmac);
	}

}

int GMAC_MacInit(Gmac_Object* gmac)
{

	gmac->p_phy_dri->startup(gmac->p_phy_dev);
	fh_gmac_sync_phy_status(gmac);
	/*dump_phy_info(gmac->p_phy_dev);*/
	fh_set_rmii_speed(gmac, gmac->speed);

	GMAC_SetMacAddress(gmac, gmac->local_mac_address);

	//receive all frames without any filters
	//SET_REG(REG_GMAC_FRAME_FILTER, (UINT32)1<<31);
	SET_REG(gmac->regs + REG_GMAC_FRAME_FILTER, 1 << 5 | 1 << 9);
	//reg6 flow control

	//interrupt

	//set reg0
	//auto_pad_crc_stripping
	//duplex_mode
	//speed
	//port_select = MII
	//transmitter_enable
	//receiver_enable
	SET_REG(gmac->regs + REG_GMAC_CONFIG,
			1 << 16 | 1 << 15 | gmac->speed << 14 | gmac->duplex << 11 | 1 << 7
					| 1 << 3 | 1 << 2);

	return 0;
}

int GMAC_Probe(Gmac_Object* gmac)
{
	UINT32 phyReg2, phyReg3, ouiVal;
	int i;
	for(i=0; i<32; i++)
	{
		phyReg2 = GMAC_GetPhyRegister(gmac, i, gmac_phyt_reg_phy_id1);
		phyReg3 = GMAC_GetPhyRegister(gmac, i, gmac_phyt_reg_phy_id2);
		ouiVal = (phyReg2 & 0xffff) << 16;
		ouiVal |= (phyReg3 & 0xffff);

		if ((ouiVal & 0x1fffffff) != 0x1fffffff)
		{
			gmac->phy_id = i;
			gmac->oui = ouiVal;
			break;
		}
        }
	if(i >= 32)
	{
		printf("ERROR: cannot get correct phyid.\n");
	}

	/*printf("PHY OUI: 0x%x at phyid: %d\n", ouiVal, gmac->phy_id);*/
	return 0;
}

int GMAC_Init(struct eth_device* dev, bd_t* bd)
{
	Gmac_Object* gmac;
	gmac = (Gmac_Object*) dev->priv;

	init_mac_addr(gmac, dev);
	if (GMAC_Probe(gmac) < 0) {
		return -1;
	}

	GMAC_PhyInit(gmac);
	GMAC_EarlyInit(gmac);

	if (GMAC_DmaInit(gmac) < 0 || GMAC_MacInit(gmac) < 0) {
		return -1;
	}

	return 0;
}

void GMAC_Halt(struct eth_device* dev)
{
	Gmac_Object* gmac;
	gmac = (Gmac_Object*) dev->priv;
	//reset
	//GMAC_SetPhyRegister(gmac->phy_id, gmac_phyt_reg_basic_ctrl, 0x1);
	//power down
	//GMAC_SetPhyRegister(gmac->phy_id, gmac_phyt_reg_basic_ctrl, 0x800);
	//clear & disable interrupts
	SET_REG(gmac->regs + REG_GMAC_STATUS, 0xffffffff);
	SET_REG(gmac->regs + REG_GMAC_INTR_EN, 0x0);
	//tx & rx disable
	SET_REG_M(gmac->regs + REG_GMAC_CONFIG, 0 << 3, 1 << 3);
	SET_REG_M(gmac->regs + REG_GMAC_CONFIG, 0 << 2, 1 << 2);
}

int GMAC_Send(struct eth_device* dev, void *packet, int length)
{
	int i = 0;
	Gmac_Object* gmac;
	gmac = (Gmac_Object*) dev->priv;

	SET_REG(gmac->regs + REG_GMAC_STATUS, 0xffffffff);

	gmac->tx_dma_descriptors->desc1.bit.buffer1_size = length;
	gmac->tx_dma_descriptors->desc2.bit.buffer_address_pointer =
			(UINT32) packet;
	gmac->tx_dma_descriptors->desc0.bit.own = 1;

	SET_REG_M(gmac->regs + REG_GMAC_OP_MODE, 1 << 13, 1 << 13);
	SET_REG(gmac->regs + REG_GMAC_TX_POLL_DEMAND, 0);

	while (gmac->tx_dma_descriptors->desc0.bit.own) {
#ifdef FH_GMAC_DEBUG
		UINT32 status;
		status = GET_REG(gmac->regs + REG_GMAC_STATUS);
		if(status & 0xa0b2)
		{
			//fatal bus error, read Error Bits status[25:23]
			//or
			//abnormal interrupt
			printf("***ERROR: tx status: %x\n", status);
			return -1;
		}
#endif

		udelay(1);
		i++;
		if (i > GMAC_TIMEOUT_SEND) {
			printf("***ERROR: send timeout\n");
			return -2;
		}

	}
#ifdef FH_GMAC_DEBUG
	if(gmac->tx_dma_descriptors->desc0.bit.error_summary)
	{
		printf("***ERROR: tx desc0: %x\n", gmac->tx_dma_descriptors->desc0.dw);
		return -2;

	}
#endif

	SET_REG_M(gmac->regs + REG_GMAC_OP_MODE, 0 << 13, 1 << 13);
	return 0;
}

int GMAC_Receive(struct eth_device* dev)
{
	int j;
	Gmac_Object* gmac;
	gmac = (Gmac_Object*) dev->priv;

	SET_REG(gmac->regs + REG_GMAC_STATUS, 0xffffffff);


	SET_REG_M(gmac->regs + REG_GMAC_OP_MODE, 1 << 1, 1 << 1);
	SET_REG(gmac->regs + REG_GMAC_RX_POLL_DEMAND, 0);

	for (j = 0; j < RX_DESC_NUM; j++) {
		if (!gmac->rx_dma_descriptors[j].desc0.bit.own) {
			int size = gmac->rx_dma_descriptors[j].desc0.bit.frame_length;
			net_process_received_packet(net_rx_packets[j], size);
			gmac->rx_dma_descriptors[j].desc0.dw = 0;
			gmac->rx_dma_descriptors[j].desc0.bit.own = 1;
		}
	}

	SET_REG_M(gmac->regs + REG_GMAC_OP_MODE, 0 << 1, 1 << 1);
	return 0;
}

static int cal_phy_array_size(struct phy_reg_cfg *p_cfg)
{
	int size = 0;

	for(; p_cfg->id != 0; p_cfg++) {
		size++;
	}

	return size;
}

int phy_find_process(Gmac_Object* gmac, struct eth_device *dev){

	int i;
	unsigned int phy_sel = 0;
	struct phy_interface_info *p_phy_info;
	struct phy_driver *p_phy_dri = gmac->p_phy_dri;
	struct phy_reg_cfg *p_cfg;
	int list_size = 0;

	p_cfg = (struct phy_reg_cfg *)gmac->p_plat->p_cfg_array;
	if (p_cfg)
		list_size = cal_phy_array_size(p_cfg);
	for(i = 0, p_phy_info = p_phy_dri->phy_info;
	i < ARRAY_SIZE(p_phy_dri->phy_info); i++){
		//only parse usr set info.if not set ,just continue.
		if(!!p_phy_info[i].phy_sel){
			//1:pin mux
			if(p_phy_info[i].phy_sel & EXTERNAL_PHY){
				phy_sel = EXTERNAL_PHY;
			}
			else if(p_phy_info[i].phy_sel & INTERNAL_PHY)
				phy_sel = INTERNAL_PHY;
			//2:phy sel
			if(p_phy_dri->phy_sel)
				p_phy_dri->phy_sel(gmac->p_phy_dev, phy_sel);
			
			//3:phy reset
			if(p_phy_info[i].phy_reset){
				p_phy_info[i].phy_reset();
			}
			//4:mac interface set mii or rmii
			if(p_phy_dri->inf_set)
				p_phy_dri->inf_set(gmac->p_phy_dev, p_phy_info[i].phy_inf);
			//5:mdio find phy
			//gmac->p_mii_bus = get_miiphy_handle(dev->name);
			gmac->p_phy_dev = phy_device_create_with_check(gmac->p_mii_bus, gmac->p_phy_dri,\
			AUTONEG_ENABLE, PHY_BASIC_FEATURES, (void *)p_cfg, list_size);
			//5:check result
			if (gmac->p_phy_dev){
				/*find vaild phy dev and set phy dev info..*/
				gmac->p_phy_dev->phy_inf = p_phy_info[i].phy_inf;
				gmac->p_phy_dev->phy_sel = p_phy_info[i].phy_sel;
				//bind driver active p_phy_info
				//gmac->p_phy_dri->sync_mac_spd = p_phy_info[i].sync_mac_spd;
				gmac->p_phy_dri->ac_phy_info = &p_phy_info[i];
				break;
			}
		}
	}
	if (!gmac->p_phy_dev){
		printf("no phy found....\n");
		return -1;
	}

	printf("find phy driver [%s]  : %s : %s\n",p_phy_dri->name, 
	gmac->p_phy_dev->phy_sel == EXTERNAL_PHY ? "external phy" : "internal phy",
	gmac->p_phy_dev->phy_inf == PHY_INTERFACE_MODE_MII ? "mii" : "rmii");
	return 0;
}

int auto_find_phy(Gmac_Object* gmac, struct eth_device *dev){

	int i;
	struct gmac_plat_info *p_info;
	//int switch_flag = 0;
	char *c_driver;
	
	//parse plat info...
	p_info = gmac->p_plat;
	if(!p_info){
		printf("NULL plat info ....\n");
		return -1;
	}

	for(i = 0, c_driver = p_info->phy_driver_list[0];
	(c_driver != 0 && i < MAX_PHY_DRIVER_SUPPORT_SIZE);
	i++, c_driver = p_info->phy_driver_list[i]){

		//bind phy driver...
		gmac->p_phy_dri = get_phy_driver_with_name(c_driver);
		if (!gmac->p_phy_dri) {
			printf("find no phy driver called [%s]\n",c_driver);
			continue;
		}
		//printf("got phy driver name [%s]\n",c_driver);
		if(!phy_find_process(gmac, dev))
			break;
	}

	if (!gmac->p_phy_dev){
		return -1;
	}
	
	if (gmac->p_phy_dev->autoneg == AUTONEG_DISABLE) {
		gmac->p_phy_dev->speed = SPEED_100;
		gmac->p_phy_dev->duplex = DUPLEX_FULL;
	}
	gmac->p_phy_dev->dev = dev;
	gmac->p_phy_dri->config(gmac->p_phy_dev);
	return 0;
}


int fh_gmac_initialize(bd_t* bis)
{
	Gmac_Object* gmac;
	struct gmac_plat_info *p_plat_info;
	struct eth_device* dev;
	volatile Gmac_Rx_DMA_Descriptors* rx_desc;
	volatile Gmac_Tx_DMA_Descriptors* tx_desc;

	p_plat_info = get_platform_eth_info(mac_idx);
	if (p_plat_info == NULL) {
		printf("null plat info..\n");
		return (-1);
	}

	gmac = (Gmac_Object *) malloc(sizeof(*gmac));
	if (gmac == NULL) {
		printf("malloc gmac failed..\n");
		return (-1);
	}
	memset(gmac, 0, sizeof(*gmac));

	dev = (struct eth_device *) malloc(sizeof(*dev));
	if (dev == NULL) {
		printf("fh_eth_initialize: Cannot allocate eth_device %d\n", 0);
		free(gmac);
		return (-1);
	}
	memset(dev, 0, sizeof(*dev));

	rx_desc = (Gmac_Rx_DMA_Descriptors *) malloc(
			sizeof(*rx_desc) * RX_DESC_NUM);
	memset((void*) rx_desc, 0, sizeof(*rx_desc));
	tx_desc = (Gmac_Tx_DMA_Descriptors *) malloc(sizeof(*tx_desc));
	memset((void*) tx_desc, 0, sizeof(*tx_desc));
	gmac->regs = p_plat_info->regs;
	gmac->id = p_plat_info->id;	
	gmac->rx_dma_descriptors = rx_desc;
	gmac->tx_dma_descriptors = tx_desc;
	gmac->phy_id = -1;
	gmac->p_plat = p_plat_info;

	sprintf(dev->name, "FH_EMAC_%d",p_plat_info->id);
	dev->init = GMAC_Init;
	dev->halt = GMAC_Halt;
	dev->send = GMAC_Send;
	dev->recv = GMAC_Receive;
	dev->priv = (void *) gmac;
	gmac->p_mii_bus = mdio_alloc();
	if(!gmac->p_mii_bus){
		printf("mdio malloc failed...\n");
		return -1;
	}
	gmac->p_mii_bus->priv = gmac;
	gmac->p_mii_bus->read = fh_gmac_miiphy_read;
	gmac->p_mii_bus->write = fh_gmac_miiphy_write;
	sprintf(gmac->p_mii_bus->name, "fh_mii_bus_%d",p_plat_info->id);
	init_mac_addr(gmac, dev);
	if(mdio_register(gmac->p_mii_bus))
		return -1;
	if(auto_find_phy(gmac, dev))
		return -1;
	if(eth_register(dev))
		return -1;
	mac_idx++;
	return 0;
}
